#ifndef CONSOLE_H
#define CONSOLE_H

#include <buildspec.h>
#include <GSTenums.h>
#include <string>
#include <boost/serialization/singleton.hpp> 
using boost::serialization::singleton;
#include <boost/thread.hpp> 
#include <boost/lexical_cast.hpp>
#include <boost/filesystem.hpp>



namespace GST{
namespace Utils{

	class ILogger;
	class FileLogger;
	class TerminalLogger;

//------------------------------------------------------------------------------

/**
	* This class allows to print output.
	* Console is a singleton object, that means there is one global instance
	* that can be accessed through getInstance().
	*
	* The class has a set of static methods that internally gets the Console
	* instance and run the method thread safe. E.g. the following example
	* can be called from anywhere (also from a thread) to report to all registered
	* ILoggers a message:
	\code
	Console::SendStatus("Hello World!");
	\endcode
	* 
	* By default Console manages two ILogger's internally:
	* -# A FileLogger
	* -# A TerminalLogger
	*
	* Activate them using
	* -# SetLogFilePath()
	* -# SetWriteToTerminal()
	* 
	* You can attach your own custom logger using the AddLogger() method. Therefore
	* simply implement the ILogger interface. Remark the notes 
	* \ref changelogNewLogger here.
	* <b>When dropping or deleting your custom logger call DropLogger() first,
	* to prevent NULL pointer exceptions when a log is fired after deletion.</b>
	* 
	* You can control the message types that Console is logging by the use of
	* SetLogLevel(). By default Console uses LOG_PUBLIC (which is a makro for
	* <b>LOG_ERROR | LOG_WARNING | LOG_STATUS</b>, see GSTenums.h ).
	* 
	* When you are using the GST API we suggest to set the LogLevel to 
	* LOG_ALL and implement you own Logger forwarding DEBUG and SQL messages
	* to the resource of you prefer. (That helps to to indicated what was 
	* done when debugging).
	*/
class Console
	: singleton<Console>
{
public:
	GST_API_EXPORT Console();
	GST_API_EXPORT ~Console();

	/** @returns (The global) instance of Console
	  *
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static Console * getInstance();		

	/**
	  * Sends a message to Console (of type status)
	  *
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static void Send( const std::string & Output);

	/**
	  * Sends a message to Console (of type warning)
	  *
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static void SendWarning( const std::string & Output);

	/**
	  * Sends a message to Console (of type error)
	  *
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static void SendError( const std::string & Output);

	/**
	  * Sends a message to Console (of type status)
	  *
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static void SendStatus( const std::string & Output);

	/**
	  * Sends a message to Console (of type debug)
	  *
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static void SendDebug( const std::string & Output);

	/**
	 * Logs and shrink sql statement (of type debug)
	 *
	 * @see SetSQLLogLength() 
	 * @note This method is thread safe.
	 */
	GST_API_EXPORT static void SendPID( const long & pid, const std::string & sql);

	/**
	  * Writes version and revision of build to console (in type status)
	  *
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static void SendBuildInfo();

	/**
	  * @returns the log level
	  * 
	  * @note This method is _not_ thread safe.	(Take care that no other 
	  *       thread is using console at the same time when calling this method.)
	  * @see GetLogLevel()
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static Utils::LogLevel GetLogLevel();

	/**
	  * Set the log log level
	  *
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static void SetLogLevel(Utils::LogLevel level);

	/**
	  * True if log level is at least Utils::LOG_DEBUG (or more detailed)
	  *
	  * @note This method is _not_ thread safe.	(Take care that no other 
	  *       thread is using console at the same time when calling this 
	  *       method.)
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static bool IsLoggingDebugMessages();   
		
	/**
	  * Adds a logger. Use this to implement you own ILogger. All 
	  * information send to Console are written to logger. If you 
	  * delete your ILogger instance, provide disable writing to 
	  * by passing DropLogger(<your pointer>) before deleting your 
	  * resource.
	  *
	  * @note Console will _not_ take the owner of logger.
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static void AddLogger(ILogger * logger);

	/**
	  * When you delete your ILogger resource drop the logger
	  * with pointer to your ILogger _before_ deleting
	  * your resource.
	  * Use this method when you want to stop writing onto your logger.
	  *
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static void DropLogger( ILogger * logger );		  		

	/**
	  * @returns number of chars in a logged sql statement (default: 1000)
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static long GetSQLLogLength();

	/**
	  * set the number of chars in a logged sql statement.
	  *
	  * A number <= 0 log the complete statements
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static void SetSQLLogLength(long val);

	/**
	  * Construct a FileLogger and append it to the console loggers.
	  * This (global) console is the owner of the FileLogger (m_fileLogger).
	  *
	  * If filePath is empty file logging is disabled.
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static void SetLogFilePath(const std::string & filePath);

	/**
	  * If this instance of Console is owning a FileLogger (set via 
	  * SetLogFilePath()), then this returns the filepath.
	  * Return an empty string otherwise.
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static std::string GetLogFilePath();

	/**
	  * If writeToTerminal set to true: Construct a TerminalLogger and 
	  * append it to the console loggers.
	  *
	  * If writeToTerminal set to false: Drops the TerminalLogger from
	  * list of loggers.
	  *
	  * This (global) console is the owner of the TerminalLogger (m_fileLogger).
	  * @note This method is thread safe.
	  */
	GST_API_EXPORT static void SetWriteToTerminal( bool writeToTerminal=true );

	/**
	 * Specify the terminal logger logging behavior. Use this to filter terminal
	 * logging e.g.
	 * <li> just to Debug messages: LOG_DEBUG </li>
	 * <li> just to Debug and Error messages: LOG_DEBUG | LOG_ERROR </li>
	 *
	 * To control the whole console behavior (reporting messages to all logger
	 * not just to terminal logger use SetLogLevel)
	 * @see Console::SetLogLevel()
	 */
	GST_API_EXPORT static void SetTerminalLogFilter(Utils::LogLevel logMode
													= LOG_ALL);

	/**
	 * Similar to SetTerminalLogFilter but set the filter to file loggger.
	 * @note Specify a file logger via Console::SetLogFilePath() first!
	 * @exception GSTRuntimeException is thrown when not file logger specified
	 * before.
	 */
	GST_API_EXPORT static void SetFileLogFilter(Utils::LogLevel logMode
												= LOG_ALL);

	///@name not thread safe (public interface)
	//@{
	GST_API_EXPORT void addLogger_impl(ILogger * logger);
	GST_API_EXPORT void dropLogger_impl(ILogger * logger);
	GST_API_EXPORT Utils::LogLevel getLogLevel_impl() const;
	GST_API_EXPORT void setLogLevel_impl(Utils::LogLevel val);
	GST_API_EXPORT bool isLoggingDebugMessages_impl() const;
	GST_API_EXPORT void setSQLLogLength_impl(long val);
	GST_API_EXPORT long getSQLLogLength_impl() const;
	GST_API_EXPORT void setLogFilePath_impl(const std::string & filePath);
	GST_API_EXPORT std::string getLogFilePath_impl() const;
	GST_API_EXPORT void setWriteToTerminal_impl( bool writeToTerminal );
	GST_API_EXPORT void setTerminalLogFilter_impl( Utils::LogLevel val );	
	GST_API_EXPORT void setFileLogFilter_impl( Utils::LogLevel val );
	//@}

protected:
	///@name not thread safe
	//@{
	std::stringstream & stripSQL(const std::string & sql, std::stringstream & msg ) const; 
	void writeMessageToLoggers( const std::string & message, LogLevel ltype );
	void writePIDToLoggers(const long & pid, const std::string & sql);
	//@}

private:
	typedef std::vector<ILogger*> LoggerContainer;
	LoggerContainer m_logger; 
	/**
	 * Optional Console can be the owner of a FileLogger
	 *
	 * @see	SetLogFilePath()
	 */
	FileLogger * m_fileLogger;
	/**
	 * Optional Console can be the owner of a TerminalLogger
	 *
	 * @see	SetLogFilePath()
	 */
	TerminalLogger * m_terminalLogger;

	static boost::mutex accessorMutex;	

	///@name config members
	//@{
	Utils::LogLevel logLevels;
	long sqlLogLength;
	//@}
};

}//of namespace Utils
}//of namespace GST

#endif